﻿/*** MikeWare Framework ************************************
* This class is part of product of MikeWare.Framework.
* 
* Author      : Mike Cheers
* Mail        : mikecheers@126.com
* Create Date : 2018/9/16
* Summary     : 
* 
* 
* Modified By : 
* Date        : 
* Mail        : 
* Comment     : 
**************************************************************/

namespace MikeWare.Components.MikeLoggor.Listeners
{
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Text;
    using System.Text.RegularExpressions;
    using MikeWare.Components.MikeLoggor.Policies;
    using System.Diagnostics;

    public class FileWriterListener : Listener
    {
        public String Root { get; private set; }
        public String CurrentLogFilePattern { get; private set; }
        public String RollingLogFilePattern { get; private set; }
        public Encoding Encodor { get; set; }
        public IList<RollingPolicy> RollingPolicies { get; private set; }

        public FileInfo CurrentLogFile { get; set; }
        public StreamWriter CurrentLogFileWriter { get; set; }
        public DateTime CurrentLogFileTime { get; set; }
        public Int64 CurrentLogRecordCount = 0;

        public FileWriterListener(Loggor loggor, Settings setting)
            : base(loggor, setting)
        {
            InitAttributes(setting.Childen);

            //if (array[1].IndexOf("$") > -1)
            //{
            //    ReplaceReferenceAttributes(ref array[1], settings);
            //}
            //if (array[1].IndexOf("%d") > -1)
            //{
            //    ReplaceDateTimeAttributes(ref array[1]);
            //}
        }

        public override void InitAttributes(IList<Settings> attributes)
        {
            if (null == attributes || 0 == attributes.Count) return;

            foreach (var attribute in attributes)
            {
                switch (attribute.Name.Trim().ToUpper())
                {
                    case "ROOT":
                        Root = attribute.Value;
                        break;
                    case "ENCODING":
                        Encodor = Encoding.GetEncoding(attribute.Value);
                        break;
                    case "CURRENTLOGFILEPATTERN":
                        CurrentLogFilePattern = attribute.Value;
                        break;
                    case "ROLLINGLOGFILEPATTERN":
                        RollingLogFilePattern = attribute.Value;
                        break;
                    case "ROLLINGPOLICY":
                        RollingPolicies = RollingPolicy.BuildRollingPolicies(attribute.Childen);
                        break;
                    default:
                        break;
                }
            }
        }

        public override void Write(DateTime logTime, string message)
        {
            Rolling();
            lock (CurrentLogFile)
            {
                CurrentLogFileWriter.Write(logTime.ToString("HH:mm:ss -> ") + message);
                System.Threading.Interlocked.Increment(ref CurrentLogRecordCount);
            }
        }

        public override void WriteLine()
        {
            Rolling();
            lock (CurrentLogFile)
            {
                CurrentLogFileWriter.WriteLine();
            }
        }

        public override void Flush()
        {
            if (null != CurrentLogFileWriter) CurrentLogFileWriter.Flush();
        }

        public override void Rolling()
        {
            if (string.IsNullOrEmpty(CurrentLogFilePattern))
                throw new ArgumentNullException("CurrentLogFilePattern");

            if (null == CurrentLogFile)
            {
                CreateLogFile();
                return;
            }

            if (string.IsNullOrEmpty(RollingLogFilePattern))
                throw new ArgumentNullException("RollingLogFilePattern");

            Boolean shouldRolling = false;

            foreach (var rollingPolicy in RollingPolicies)
                if (rollingPolicy.ShouldRolling(this)) { Trace.WriteLine("Rolling by " + rollingPolicy.Setting.Name); shouldRolling = true; break; }

            if (shouldRolling)
            {
                Close();

                var rollingFileFullName = RollingLogFilePattern;
                ReplaceDateTimeAttributes(ref rollingFileFullName, CurrentLogFileTime);
                ReplaceReferenceAttributes(ref rollingFileFullName);

                var rollingFile = new FileInfo(rollingFileFullName);
                if (!rollingFile.Directory.Exists) rollingFile.Directory.Create();

                var files = rollingFile.Directory.GetFiles();
                var rollingIndex = -1;
                if (null != files && 0 < files.Length)
                {
                    var rollingFilePattern = rollingFileFullName
                        .Replace(rollingFile.Directory.FullName, String.Empty)
                        .TrimStart(@"\".ToCharArray())
                        .Replace("%i", @"(?<index>\d+)");
                    Regex regex = new Regex(rollingFilePattern);
                    foreach (var file in files)
                    {
                        var match = regex.Match(file.Name);
                        if (match.Success)
                            rollingIndex = Math.Max(rollingIndex, Int32.Parse(match.Groups["index"].Value));
                    }
                }
                rollingIndex++;

                rollingFileFullName = rollingFileFullName.Replace("%i", rollingIndex.ToString());

                CurrentLogFile.MoveTo(rollingFileFullName);

                CreateLogFile();
            }
        }

        private void CreateLogFile()
        {
            Close();

            CurrentLogRecordCount = 0;
            CurrentLogFileTime = DateTime.Now;

            var currentFileFullName = CurrentLogFilePattern;
            ReplaceDateTimeAttributes(ref currentFileFullName, CurrentLogFileTime);
            ReplaceReferenceAttributes(ref currentFileFullName);

            CurrentLogFile = new FileInfo(currentFileFullName);
            if (!CurrentLogFile.Directory.Exists) CurrentLogFile.Directory.Create();
            CurrentLogFileWriter = new StreamWriter(CurrentLogFile.FullName, true, Encodor);
            CurrentLogFileWriter.AutoFlush = true;
        }

        public override void Close()
        {
            if (null != CurrentLogFileWriter)
                CurrentLogFileWriter.Close();
        }
    }
}
